#include "..\inc\Data.h"
/**
 * @brief default constructor, initialize the data_matrix_ to 4*4
 */
Data::Data()
{
    this->data_matrix_ = new matrix;
    for (int i = 0; i < 4; i++)
    {
        this->data_matrix_->emplace_back(4, 0);
    }
    this->m_newRow    = -1;
    this->m_newColumn = -1;
}

/**
 * @brief destructor, delete the data_matrix_
 */
Data::~Data()
{
    delete this->data_matrix_;
}

/**
 * @brief copy constructor, copy the data_matrix_
 */
Data::Data(const Data &data)
{
    this->data_matrix_ = new matrix;
    for (int i = 0; i < 4; i++)
    {
        this->data_matrix_->emplace_back(4, 0);
    }
    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            this->data_matrix_->at(i).at(j) = data.data_matrix_->at(i).at(j);
        }
    }
    this->m_newRow    = data.m_newRow;
    this->m_newColumn = data.m_newColumn;
}

/**
 * @brief initialize the data_matrix_ with random number 2 or 4
 */
void Data::random_init()
{
    this->clear();

    // Generate two random positions
    std::random_device rd;
    std::default_random_engine e(rd() * time(nullptr));
    std::uniform_int_distribution<int> distribution(0, 3);
    int rand_row_1    = distribution(e);
    int rand_column_1 = distribution(e);
    int rand_row_2    = distribution(e);
    int rand_column_2 = distribution(e);

    // set the value of first random position "4"
    this->data_matrix_->at(rand_row_1).at(rand_column_1) = 4;

    // set the value of second random position "2"
    while (rand_row_1 == rand_row_2 && rand_column_1 == rand_column_2)
    {
        rand_row_2    = distribution(e);
        rand_column_2 = distribution(e);
    }
    this->data_matrix_->at(rand_row_2).at(rand_column_2) = 2;
}

/**
 * @brief clear the data_matrix_
 */
void Data::clear()
{
    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 4; j++)
        {

            this->data_matrix_->at(i).at(j) = 0;
        }
    }
}

/**
 * @brief get the number at the specific position(row, column)
 * @param row
 * @param column
 * @return the number at the specific position(row, column)
 */
int Data::get_number(const int &row, const int &column) const
{
    return this->data_matrix_->at(row).at(column);
}

/**
 * @brief set the number at the specific position(row, column)
 * @param row
 * @param column
 * @param number_input
 * @return true if the number_input is valid, false otherwise
 */
bool Data::set_number(const int &row, const int &column, int number_input)
{
    if (row >= 0 && row <= 3 && column >= 0 && column <= 3)
    {
        switch (number_input)
        {
            case 0:
            case 2:
            case 4:
            case 8:
            case 16:
            case 32:
            case 64:
            case 128:
            case 256:
            case 512:
            case 1024:
            case 2048:
                this->data_matrix_->at(row).at(column) = number_input;
                return true;
            default:
                return false;
        }
    }
    else
    {
        return false;
    }
}

/**
 * @brief check if the specific position(row, column) is empty
 * @param row
 * @param column
 * @return is the specific position(row, column) is empty
 */
bool Data::is_empty(const int &row, const int &column) const
{
    return this->data_matrix_->at(row).at(column) == 0;
}

Data &Data::operator=(const Data &data_input)
{
    // self assignment
    if (this == &data_input)
        return *this;

    this->data_matrix_->clear();
    for (int i = 0; i < 4; i++)
    {
        this->data_matrix_->emplace_back(4, 0);
    }
    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            this->data_matrix_->at(i).at(j) = data_input.data_matrix_->at(i).at(j);
        }
    }
    this->m_newRow    = data_input.m_newRow;
    this->m_newColumn = data_input.m_newColumn;
    return *this;
}
